#include "stdafx.h"
#include "FourierTransformFactoryIppFFT.h"
#include "includeIpp.h"
#include "IppUtils.h"
#include "fftutils.h"

using namespace FTLib;

// TODO: no, the packed layout of IPP is different from FFTW, we need an extra interface for this...
class CRealToComplex_PackedIppComplexIppFft : public IRealToComplex_PackedIppComplex
{
protected:
	uint32_t width, height;
private:
	bool	isFft;
	int orderX, orderY;

	union
	{
		IppiFFTSpec_R_32f* pFFTSpec;
		IppiDFTSpec_R_32f* pDFTSpec;
		void* pSpecBuffer;
	};

	Ipp8u* pBuffer;
	float factorMultiply;
public:
	CRealToComplex_PackedIppComplexIppFft() = delete;
	CRealToComplex_PackedIppComplexIppFft(uint32_t width, uint32_t height, double normalizationFactor) : isFft(false), width(width), height(height)
	{
		// we do not distinguish the flag-enum for FFT and DFT since they have the same
		// numeric value - this is what we verify here
		static_assert(IPP_FFT_DIV_FWD_BY_N == IPP_DIV_FWD_BY_N && IPP_FFT_DIV_INV_BY_N == IPP_DIV_INV_BY_N &&
			IPP_FFT_DIV_BY_SQRTN == IPP_DIV_BY_SQRTN && IPP_FFT_NODIV_BY_ANY == IPP_NODIV_BY_ANY,
			"Flags for FFT and DFT are assumed to be the same.");

		int normalizationFlag;
		float normFactor;
		if (IsNormalizationFactorDivBySize(normalizationFactor))
		{
			normalizationFlag = IPP_FFT_DIV_FWD_BY_N;
			normFactor = 1;
		}
		else if (IsNormalizationFactorDivBySqrtSize(normalizationFactor))
		{
			normalizationFlag = IPP_FFT_DIV_BY_SQRTN;
			normFactor = 1;
		}
		else
		{
			normalizationFlag = IPP_FFT_NODIV_BY_ANY;
			normFactor = (float)normalizationFactor;
		}

		int orderX = IppUtils::CalcOrder(width);
		if (orderX >= 0)
		{
			int orderY = IppUtils::CalcOrder(height);
			if (orderY >= 0)
			{
				this->isFft = true;
				this->orderX = orderX;
				this->orderY = orderY;
			}
		}

		IppStatus status;
		int sizeSpec, sizeInit, sizeBuf;
		if (this->isFft == true)
		{
			status = ippiFFTGetSize_R_32f(orderX, orderY, normalizationFlag, ippAlgHintNone, &sizeSpec, &sizeInit, &sizeBuf);
			this->pFFTSpec = (IppiFFTSpec_R_32f*)ippMalloc(sizeSpec);
			this->pBuffer = (sizeBuf > 0) ? (Ipp8u*)ippMalloc(sizeBuf) : nullptr;
			Ipp8u* pMemInit = (sizeInit > 0) ? (Ipp8u*)ippMalloc(sizeInit) : nullptr;
			status = ippiFFTInit_R_32f(orderX, orderY, normalizationFlag, ippAlgHintNone, this->pFFTSpec, pMemInit);
			if (pMemInit != nullptr) { ippFree(pMemInit); }
		}
		else
		{
			IppiSize size = { width, height };
			status = ippiDFTGetSize_R_32f(size, normalizationFlag, ippAlgHintNone, &sizeSpec, &sizeInit, &sizeBuf);
			this->pDFTSpec = (IppiDFTSpec_R_32f*)ippMalloc(sizeSpec);
			this->pBuffer = (sizeBuf > 0) ? (Ipp8u*)ippMalloc(sizeBuf) : nullptr;
			Ipp8u* pMemInit = (sizeInit > 0) ? (Ipp8u*)ippMalloc(sizeInit) : nullptr;
			status = ippiDFTInit_R_32f(size, normalizationFlag, ippAlgHintNone, this->pDFTSpec, pMemInit);
			if (pMemInit != nullptr) { ippFree(pMemInit); }
		}

		this->factorMultiply = normFactor;
	}

	~CRealToComplex_PackedIppComplexIppFft()
	{
		if (this->pBuffer != nullptr)
		{
			ippFree(this->pBuffer);
		}

		if (this->pSpecBuffer != nullptr)
		{
			ippFree(this->pSpecBuffer);
		}
	}

	virtual void RealToComplex_PackedIppComplex(const FourierTransfRealInputData2d& input, const FourierTransfComplexOutput2d& output, const FourierTransformAdditional* pAdditional)
	{
		IppStatus status;
		if (this->isFft == true)
		{
			status = ippiFFTFwd_RToPack_32f_C1R(input.ptrData, input.pitch, output.ptrData, output.pitch, this->pFFTSpec, this->pBuffer);
		}
		else
		{
			status = ippiDFTFwd_RToPack_32f_C1R(input.ptrData, input.pitch, output.ptrData, output.pitch, this->pDFTSpec, this->pBuffer);
		}

		if (this->factorMultiply != 1)
		{
			this->MultiplyWithFactor(output);
		}
	}

private:
	void MultiplyWithFactor(const FourierTransfComplexOutput2d& output)
	{
		IppiSize size = { output.width, output.height };
		ippiDivC_32f_C1IR(this->factorMultiply, output.ptrData, output.pitch, size);
	}
};

class CRealToComplex_ComplexIppFft :public IRealToComplex_Complex, private CRealToComplex_PackedIppComplexIppFft
{
private:
	Ipp32f* packedResultBuffer;
public:
	CRealToComplex_ComplexIppFft(uint32_t width, uint32_t height, double normalizationFactor) :CRealToComplex_PackedIppComplexIppFft(width, height, normalizationFactor)
	{
		this->packedResultBuffer = (Ipp32f*)ippMalloc(this->width*this->height*sizeof(Ipp32f));
	};

	~CRealToComplex_ComplexIppFft()
	{
		ippFree(this->packedResultBuffer);
	}

	virtual void RealToComplex_Complex(const FourierTransfRealInputData2d& input, const FourierTransfComplexOutput2d& output, const FourierTransformAdditional* pAdditional)
	{
		FourierTransfComplexOutput2d outputTemp;
		outputTemp.width = this->width;
		outputTemp.height = this->height;
		outputTemp.pitch = this->width*sizeof(float);
		outputTemp.ptrData = this->packedResultBuffer;
		//outputTemp.ptrData = output.ptrData;	... no, ippiPackToCplxExtend_32f32fc_C1R doesn't work in-place
		this->RealToComplex_PackedIppComplex(input, outputTemp, pAdditional);
		IppiSize size = { this->width, this->height };
		IppStatus status = ippiPackToCplxExtend_32f32fc_C1R(outputTemp.ptrData, size, outputTemp.pitch, (Ipp32fc*)output.ptrData, output.pitch);
	}
};

class CCreateRealToComplexIppFft_Shifted :public IRealToComplex_Complex
{
public:
	CCreateRealToComplexIppFft_Shifted(){};

	~CCreateRealToComplexIppFft_Shifted(){};

	virtual void RealToComplex_Complex(const FourierTransfRealInputData2d& input, const FourierTransfComplexOutput2d& output, const FourierTransformAdditional* pAdditional){};
};

class CCreateComplexToComplexIppFftBase
{
protected:
	uint32_t width, height;
	bool	isFft;
	int orderX, orderY;

	union {
		IppiFFTSpec_C_32fc* pFFTSpec;
		IppiDFTSpec_C_32fc* pDFTSpec;
		void* pSpecBuffer;
	};

	Ipp8u* pBuffer;

	float multiplyResultWith;
public:
	CCreateComplexToComplexIppFftBase() = delete;
	CCreateComplexToComplexIppFftBase(uint32_t width, uint32_t height, int normalizationFlag) : isFft(false), width(width), height(height), multiplyResultWith(1)
	{
		int orderX = IppUtils::CalcOrder(width);
		if (orderX >= 0)
		{
			int orderY = IppUtils::CalcOrder(height);
			if (orderY >= 0)
			{
				this->isFft = true;
				this->orderX = orderX;
				this->orderY = orderY;
			}
		}

		IppStatus status;
		int sizeSpec, sizeInit, sizeBuf;
		if (this->isFft == true)
		{
			status = ippiFFTGetSize_C_32fc(orderX, orderY, normalizationFlag, ippAlgHintNone, &sizeSpec, &sizeInit, &sizeBuf);
			this->pFFTSpec = (IppiFFTSpec_C_32fc*)ippMalloc(sizeSpec);
			this->pBuffer = (sizeBuf > 0) ? (Ipp8u*)ippMalloc(sizeBuf) : nullptr;
			Ipp8u* pMemInit = (sizeInit > 0) ? (Ipp8u*)ippMalloc(sizeInit) : nullptr;
			status = ippiFFTInit_C_32fc(orderX, orderY, normalizationFlag, ippAlgHintNone, this->pFFTSpec, pMemInit);
			if (pMemInit != nullptr) { ippFree(pMemInit); }
		}
		else
		{
			IppiSize size = { width, height };
			status = ippiDFTGetSize_C_32fc(size, normalizationFlag, ippAlgHintNone, &sizeSpec, &sizeInit, &sizeBuf);
			this->pDFTSpec = (IppiDFTSpec_C_32fc*)ippMalloc(sizeSpec);
			this->pBuffer = (sizeBuf > 0) ? (Ipp8u*)ippMalloc(sizeBuf) : nullptr;
			Ipp8u* pMemInit = (sizeInit > 0) ? (Ipp8u*)ippMalloc(sizeInit) : nullptr;
			status = ippiDFTInit_C_32fc(size, normalizationFlag, ippAlgHintNone, this->pDFTSpec, pMemInit);
			if (pMemInit != nullptr) { ippFree(pMemInit); }
		}
	}

	~CCreateComplexToComplexIppFftBase()
	{
		if (this->pBuffer != nullptr)
		{
			ippFree(this->pBuffer);
		}

		if (this->pSpecBuffer != nullptr)
		{
			ippFree(this->pSpecBuffer);
		}
	}
protected:
	void MultiplyResult(float* ptr, int pitch, int width, int height)
	{
		if (this->multiplyResultWith != 1)
		{
			IppiSize size = { 2 * width, height };
			ippiDivC_32f_C1IR(this->multiplyResultWith, ptr, pitch, size);
		}
	}
};

class CCreateComplexToComplexFwdIppFft :public CCreateComplexToComplexIppFftBase, public IComplexToComplexFwd_Complex
{
public:
	CCreateComplexToComplexFwdIppFft() = delete;
	CCreateComplexToComplexFwdIppFft(uint32_t width, uint32_t height, double normalizationFactor) :CCreateComplexToComplexIppFftBase(width, height, DetermineNormalizationFlag(normalizationFactor))
	{
		if (!IsNormalizationFactorDivBySize(normalizationFactor) && !IsNormalizationFactorDivBySqrtSize(normalizationFactor) && normalizationFactor != 1)
		{
			this->multiplyResultWith = (float)normalizationFactor;
		}
	}

	virtual void ComplexToComplex_Complex(const FourierTransfComplexInput2d& input, const FourierTransfComplexOutput2d& output, const FourierTransformAdditional* pAdditional)
	{
		IppStatus status;
		if (this->isFft == true)
		{
			status = ippiFFTFwd_CToC_32fc_C1R((const Ipp32fc*)input.ptrData, input.pitch, (Ipp32fc*)output.ptrData, output.pitch, this->pFFTSpec, this->pBuffer);
		}
		else
		{
			status = ippiDFTFwd_CToC_32fc_C1R((const Ipp32fc*)input.ptrData, input.pitch, (Ipp32fc*)output.ptrData, output.pitch, this->pDFTSpec, this->pBuffer);
		}

		this->MultiplyResult(output.ptrData, output.pitch, output.width, output.height);
	}
private:
	static int DetermineNormalizationFlag(double normalizationFactor)
	{
		if (IsNormalizationFactorDivBySize(normalizationFactor))
		{
			return IPP_FFT_DIV_FWD_BY_N;
		}

		if (IsNormalizationFactorDivBySqrtSize(normalizationFactor))
		{
			return IPP_FFT_DIV_BY_SQRTN;
		}

		return IPP_FFT_NODIV_BY_ANY;
	}
};

class CCreateComplexToComplexInvIppFft : public CCreateComplexToComplexIppFftBase, public IComplexToComplexInv_Complex
{
public:
	CCreateComplexToComplexInvIppFft() = delete;
	CCreateComplexToComplexInvIppFft(uint32_t width, uint32_t height, double normalizationFactor) :CCreateComplexToComplexIppFftBase(width, height, DetermineNormalizationFlag(normalizationFactor))
	{
		if (!IsNormalizationFactorDivBySize(normalizationFactor) && !IsNormalizationFactorDivBySqrtSize(normalizationFactor) && normalizationFactor != 1)
		{
			this->multiplyResultWith = (float)normalizationFactor;
		}
	}

	virtual void ComplexToComplex_Complex(const FourierTransfComplexInput2d& input, const FourierTransfComplexOutput2d& output, const FourierTransformAdditional* pAdditional)
	{
		IppStatus status;
		if (this->isFft == true)
		{
			status = ippiFFTInv_CToC_32fc_C1R((const Ipp32fc*)input.ptrData, input.pitch, (Ipp32fc*)output.ptrData, output.pitch, this->pFFTSpec, this->pBuffer);
		}
		else
		{
			status = ippiDFTInv_CToC_32fc_C1R((const Ipp32fc*)input.ptrData, input.pitch, (Ipp32fc*)output.ptrData, output.pitch, this->pDFTSpec, this->pBuffer);
		}

		this->MultiplyResult(output.ptrData, output.pitch, output.width, output.height);
	}
private:
	static int DetermineNormalizationFlag(double normalizationFactor)
	{
		if (IsNormalizationFactorDivBySize(normalizationFactor))
		{
			return IPP_FFT_DIV_INV_BY_N;
		}

		if (IsNormalizationFactorDivBySqrtSize(normalizationFactor))
		{
			return IPP_FFT_DIV_BY_SQRTN;
		}

		return IPP_FFT_NODIV_BY_ANY;
	}
};

class CCreateComplexToRealIppFftBase
{
protected:
	uint32_t width, height;
	bool	isFft;
	int orderX, orderY;

	union {
		IppiFFTSpec_R_32f* pFFTSpec;
		IppiDFTSpec_R_32f* pDFTSpec;
		void* pSpecBuffer;
	};

	Ipp8u* pBuffer;

	float multiplyResultWith;
public:
	CCreateComplexToRealIppFftBase() = delete;
	CCreateComplexToRealIppFftBase(uint32_t width, uint32_t height, int normalizationFlag) : isFft(false), width(width), height(height), multiplyResultWith(1)
	{
		int orderX = IppUtils::CalcOrder(width);
		if (orderX >= 0)
		{
			int orderY = IppUtils::CalcOrder(height);
			if (orderY >= 0)
			{
				this->isFft = true;
				this->orderX = orderX;
				this->orderY = orderY;
			}
		}

		IppStatus status;
		int sizeSpec, sizeInit, sizeBuf;
		if (this->isFft == true)
		{
			status = ippiFFTGetSize_R_32f(orderX, orderY, normalizationFlag, ippAlgHintNone, &sizeSpec, &sizeInit, &sizeBuf);
			this->pFFTSpec = (IppiFFTSpec_R_32f*)ippMalloc(sizeSpec);
			this->pBuffer = (sizeBuf > 0) ? (Ipp8u*)ippMalloc(sizeBuf) : nullptr;
			Ipp8u* pMemInit = (sizeInit > 0) ? (Ipp8u*)ippMalloc(sizeInit) : nullptr;
			status = ippiFFTInit_R_32f(orderX, orderY, normalizationFlag, ippAlgHintNone, this->pFFTSpec, pMemInit);
			if (pMemInit != nullptr) { ippFree(pMemInit); }
		}
		else
		{
			IppiSize size = { width, height };
			status = ippiDFTGetSize_R_32f(size, normalizationFlag, ippAlgHintNone, &sizeSpec, &sizeInit, &sizeBuf);
			this->pDFTSpec = (IppiDFTSpec_R_32f*)ippMalloc(sizeSpec);
			this->pBuffer = (sizeBuf > 0) ? (Ipp8u*)ippMalloc(sizeBuf) : nullptr;
			Ipp8u* pMemInit = (sizeInit > 0) ? (Ipp8u*)ippMalloc(sizeInit) : nullptr;
			status = ippiDFTInit_R_32f(size, normalizationFlag, ippAlgHintNone, this->pDFTSpec, pMemInit);
			if (pMemInit != nullptr) { ippFree(pMemInit); }
		}
	}

	~CCreateComplexToRealIppFftBase()
	{
		if (this->pBuffer != nullptr)
		{
			ippFree(this->pBuffer);
		}

		if (this->pSpecBuffer != nullptr)
		{
			ippFree(this->pSpecBuffer);
		}
	}
protected:
	void MultiplyResult(float* ptr, int pitch, int width, int height)
	{
		if (this->multiplyResultWith != 1)
		{
			IppiSize size = { width, height };
			ippiDivC_32f_C1IR(this->multiplyResultWith, ptr, pitch, size);
		}
	}
};

class CComplexHermitianToRealIppFft : public CCreateComplexToRealIppFftBase, public IComplexHermitianToReal
{
private:
	float* ptrTemp;
	int tempStride;
public:
	CComplexHermitianToRealIppFft() = delete;
	CComplexHermitianToRealIppFft(uint32_t width, uint32_t height, double normalizationFactor) :
		CCreateComplexToRealIppFftBase(width, height, DetermineNormalizationFlag(normalizationFactor))
	{
		if (!IsNormalizationFactorDivBySize(normalizationFactor) && !IsNormalizationFactorDivBySqrtSize(normalizationFactor) && normalizationFactor != 1)
		{
			this->multiplyResultWith = (float)normalizationFactor;
		}

		this->ptrTemp = ippiMalloc_32f_C1(width, height, &this->tempStride);
	}

	~CComplexHermitianToRealIppFft()
	{
		if (this->ptrTemp != nullptr)
		{
			ippFree(this->ptrTemp);
		}
	}

	virtual void ComplexHermitianToReal(const FourierTransfComplexInput2d& input, const FourierTransfRealOutputData2d& output, const FourierTransformAdditional* pAdditional)
	{
		// we must first convert the input into RCPack2D-format, the input is assumed to be hermitian
		// (which we do not verify here!)
		IppiSize size = { this->width, this->height };
		IppStatus status = ippiCplxExtendToPack_32fc32f_C1R((const Ipp32fc*)input.ptrData, input.pitch, size, this->ptrTemp, this->tempStride);
		if (this->isFft == true)
		{
			status = ippiFFTInv_PackToR_32f_C1R((const Ipp32f*)this->ptrTemp, this->tempStride, (Ipp32f*)output.ptrData, output.pitch, this->pFFTSpec, this->pBuffer);
		}
		else
		{
			status = ippiDFTInv_PackToR_32f_C1R((const Ipp32f*)this->ptrTemp, this->tempStride, (Ipp32f*)output.ptrData, output.pitch, this->pDFTSpec, this->pBuffer);
		}

		this->MultiplyResult(output.ptrData, output.pitch, output.width, output.height);
	}
private:
	void MultiplyResult(float* ptr, int pitch, int width, int height)
	{
		if (this->multiplyResultWith != 1)
		{
			IppiSize size = { width, height };
			ippiDivC_32f_C1IR(this->multiplyResultWith, ptr, pitch, size);
		}
	}

	static int DetermineNormalizationFlag(double normalizationFactor)
	{
		if (IsNormalizationFactorDivBySize(normalizationFactor))
		{
			return IPP_FFT_DIV_FWD_BY_N;
		}

		if (IsNormalizationFactorDivBySqrtSize(normalizationFactor))
		{
			return IPP_FFT_DIV_BY_SQRTN;
		}

		return IPP_FFT_NODIV_BY_ANY;
	}
};

#if 0
class CComplexHermetianToRealIppFft : public IComplexHermetianToReal
{
protected:
	uint32_t width, height;
private:
	bool	isFft;
	int orderX, orderY;

	float* ptrTemp;
	int tempStride;


	union
	{
		IppiFFTSpec_R_32f* pFFTSpec;
		IppiDFTSpec_R_32f* pDFTSpec;
		void* pSpecBuffer;
	};

	Ipp8u* pBuffer;
	float factorMultiply;
public:
	CComplexHermetianToRealIppFft() = delete;
	CComplexHermetianToRealIppFft(uint32_t width, uint32_t height, double normalizationFactor) : isFft(false), width(width), height(height), ptrTemp(nullptr)
	{
		// we do not distinguish the flag-enum for FFT and DFT since they have the same
		// numeric value - this is what we verify here
		static_assert(IPP_FFT_DIV_FWD_BY_N == IPP_DIV_FWD_BY_N && IPP_FFT_DIV_INV_BY_N == IPP_DIV_INV_BY_N &&
			IPP_FFT_DIV_BY_SQRTN == IPP_DIV_BY_SQRTN && IPP_FFT_NODIV_BY_ANY == IPP_NODIV_BY_ANY,
			"Flags for FFT and DFT are assumed to be the same.");

		int normalizationFlag;
		float normFactor;
		if (IsNormalizationFactorDivBySize(normalizationFactor))
		{
			normalizationFlag = IPP_FFT_DIV_FWD_BY_N;
			normFactor = 1;
		}
		else if (IsNormalizationFactorDivBySqrtSize(normalizationFactor))
		{
			normalizationFlag = IPP_FFT_DIV_BY_SQRTN;
			normFactor = 1;
		}
		else
		{
			normalizationFlag = IPP_FFT_NODIV_BY_ANY;
			normFactor = (float)normalizationFactor;
		}

		int orderX = IppUtils::CalcOrder(width);
		if (orderX >= 0)
		{
			int orderY = IppUtils::CalcOrder(height);
			if (orderY >= 0)
			{
				this->isFft = true;
				this->orderX = orderX;
				this->orderY = orderY;
			}
		}

		IppStatus status;
		int sizeSpec, sizeInit, sizeBuf;
		if (this->isFft == true)
		{
			status = ippiFFTGetSize_R_32f(orderX, orderY, normalizationFlag, ippAlgHintNone, &sizeSpec, &sizeInit, &sizeBuf);
			this->pFFTSpec = (IppiFFTSpec_R_32f*)ippMalloc(sizeSpec);
			this->pBuffer = (sizeBuf > 0) ? (Ipp8u*)ippMalloc(sizeBuf) : nullptr;
			Ipp8u* pMemInit = (sizeInit > 0) ? (Ipp8u*)ippMalloc(sizeInit) : nullptr;
			status = ippiFFTInit_R_32f(orderX, orderY, normalizationFlag, ippAlgHintNone, this->pFFTSpec, pMemInit);
			if (pMemInit != nullptr) { ippFree(pMemInit); }
		}
		else
		{
			IppiSize size = { width, height };
			status = ippiDFTGetSize_R_32f(size, normalizationFlag, ippAlgHintNone, &sizeSpec, &sizeInit, &sizeBuf);
			this->pDFTSpec = (IppiDFTSpec_R_32f*)ippMalloc(sizeSpec);
			this->pBuffer = (sizeBuf > 0) ? (Ipp8u*)ippMalloc(sizeBuf) : nullptr;
			Ipp8u* pMemInit = (sizeInit > 0) ? (Ipp8u*)ippMalloc(sizeInit) : nullptr;
			status = ippiDFTInit_R_32f(size, normalizationFlag, ippAlgHintNone, this->pDFTSpec, pMemInit);
			if (pMemInit != nullptr) { ippFree(pMemInit); }
		}

		this->factorMultiply = normFactor;

		this->ptrTemp = ippiMalloc_32f_C1(width, height, &this->tempStride);
	}

	~CComplexHermetianToRealIppFft()
	{
		if (this->pBuffer != nullptr)
		{
			ippFree(this->pBuffer);
		}

		if (this->pSpecBuffer != nullptr)
		{
			ippFree(this->pSpecBuffer);
		}

		if (this->ptrTemp != nullptr)
		{
			ippFree(this->ptrTemp);
		}
	}

	virtual void ComplexHermetianToReal(const FourierTransfComplexInput2d& input, const FourierTransfRealOutputData2d& output)
	{
		// we must first convert the input into RCPack2D-format, the input is assumed to be hermetian
		// (which we do not verify here!)
		IppiSize size = { this->width, this->height };
		IppStatus status = ippiCplxExtendToPack_32fc32f_C1R((const Ipp32fc*)input.ptrData, input.pitch, size, this->ptrTemp, this->tempStride);
		if (this->isFft == true)
		{
			status = ippiFFTInv_PackToR_32f_C1R((const Ipp32f*)this->ptrTemp, this->tempStride, (Ipp32f*)output.ptrData, output.pitch, this->pFFTSpec, this->pBuffer);
		}
		else
		{
			status = ippiDFTInv_PackToR_32f_C1R((const Ipp32f*)input.ptrData, input.pitch, (Ipp32f*)output.ptrData, output.pitch, this->pDFTSpec, this->pBuffer);
		}

		/*	if (this->multiplyResultWith != 1)
			{
			IppiSize size = { 2 * width, height };
			ippiDivC_32f_C1IR(this->multiplyResultWith, ptr, pitch, size);
			}*/
		////throw std::logic_error("The method or operation is not implemented.");
	}
};
#endif

//------------------------------------------------------------------------------

/*static*/void CFourierTransformFactoryIppFft::GetVersionString(std::wstring& str)
{
	const IppLibraryVersion* version = ippiGetLibVersion();

	std::wstringstream ss;
	ss << L"major=" << version->major << L", minor=" << version->minor << L", name=\"" << version->Name << L"\", version=\"" << version->Version << L"\", build date=\"" << version->BuildDate << "\"";
	str = ss.str();
}

/*static*/bool CFourierTransformFactoryIppFft::CanLoadLibrary()
{
	const IppLibraryVersion* version = ippiGetLibVersion();
	return true;
}

/*static*/bool CFourierTransformFactoryIppFft::CanOperate()
{
	return true;
}

/*virtual*/std::shared_ptr<IRealToComplex_PackedFftwComplex> CFourierTransformFactoryIppFft::CreateRealToComplexPackedFftw(const Options& opts)
{
	// not implemented
	return std::shared_ptr<IRealToComplex_PackedFftwComplex>();
}

/*virtual*/std::shared_ptr<IRealToComplex_PackedIppComplex> CFourierTransformFactoryIppFft::CreateRealToComplexPackedIpp(const Options& opts)
{
	return std::make_shared<CRealToComplex_PackedIppComplexIppFft>(opts.width, opts.height, opts.normalizationFactor);
}

/*virtual*/std::shared_ptr<IRealToComplex_Complex> CFourierTransformFactoryIppFft::CreateRealToComplex(const Options& opts)
{
	return std::make_shared<CRealToComplex_ComplexIppFft>(opts.width, opts.height, opts.normalizationFactor);
}

/*virtual*/std::shared_ptr<IRealToComplex_Complex> CFourierTransformFactoryIppFft::CreateRealToComplex_Shifted(const Options& opts)
{
	/*if (opts.width % 2 != 0 || opts.height % 2 != 0)
	{
	throw std::runtime_error("Only implemented for even width and height.");
	}*/

	//cufftHandle handle = cuFft_CreatePlan_2dRealToComplex_ZeroFreqInMiddle(opts.width, opts.height);
	return std::make_shared<CCreateRealToComplexIppFft_Shifted>();
}

/*virtual*/std::shared_ptr<IComplexToComplexFwd_Complex> CFourierTransformFactoryIppFft::CreateComplexToComplexFwd(const Options& opts)
{
	return std::make_shared<CCreateComplexToComplexFwdIppFft>(opts.width, opts.height, opts.normalizationFactor);
}

/*virtual*/std::shared_ptr<IComplexToComplexInv_Complex> CFourierTransformFactoryIppFft::CreateComplexToComplexInv(const Options& opts)
{
	return std::make_shared<CCreateComplexToComplexInvIppFft>(opts.width, opts.height, opts.normalizationFactor);
}

/*virtual*/std::shared_ptr<IComplexHermitianToReal> CFourierTransformFactoryIppFft::CreateComplexHermitianToReal(const Options& opts)
{
	return std::make_shared<CComplexHermitianToRealIppFft>(opts.width, opts.height, opts.normalizationFactor);
}